/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package es.miguelgonzalez.jgraficacomida;

import java.io.Serializable;
import java.util.ArrayList;

/**
 * Contiene los datos del modelo de la población; la alimentación inicial,
 * el día máximo que contiene la alimentación máxima, la alimentación máxima,
 * y la alimentación final, el número de días, el limite inferior y superior
 * de comida.
 * @author Miguel González - Ceura
 */
public class JGraficaComidaModel implements Serializable {
    private int alimentoInicial;
    private int diaMax;
    private int alimentoMax;
    private int alimentoFinal;
    
    private int numDias;
    private int limiteInferiorComida;
    private int limiteSuperiorComida;
    
    //Contiene todas las suscripciones a este modelo para que lo escuchen
    ArrayList<GraficaComidaChangeEventListener> experimentoListener;
    
    /**
     * Construye un modelo de la población con valores por defecto:
     * alimentoInicial = 150, diaMax = 15, alimentoMax = 150,
     * alimentoFinal = 150, numDias = 30, limiteInferiorComida = 0,
     * limiteSuperiorComida = 300
     */
    public JGraficaComidaModel() {
        //Establecemos los valores por defecto
        alimentoInicial = 150;
        diaMax = 15;
        alimentoMax = 150;
        alimentoFinal = 150;
        
        numDias = 30;
        limiteInferiorComida = 0;
        limiteSuperiorComida = 300;
        
        //Inicializamos el manejador de eventos
        experimentoListener = new ArrayList<GraficaComidaChangeEventListener>();
    }
    
    /**
     * Construye el modelo de la población recibiendo los datos iniciales.
     * @param numDias Número de días de la gráfica, mínimo tienen que ser 3
     * @param limiteInferiorComida No puede ser negativo
     * @param limiteSuperiorComida Tiene que ser mayor o igual que el inferior
     * @throws GraficaComidaIncorrectValueException Si no se cumplen las reglas de los
     * valores al crear el modelo (ej. limiteSuperiorComida < limiteInferiorComida)
     * se lanza la excepción.
     */
    public JGraficaComidaModel(int numDias, int limiteInferiorComida,
            int limiteSuperiorComida) throws GraficaComidaIncorrectValueException {
        
        if(numDias < 3) {
            throw new GraficaComidaIncorrectValueException("Al menos tiene que"
                    + " haber 3 días");
        }
        
        if(limiteInferiorComida < 0) {
            throw new GraficaComidaIncorrectValueException("El límite inferior "
                    + "de comida no puede ser menor que cero");
        }
        
        if(limiteSuperiorComida < limiteInferiorComida) {
            throw new GraficaComidaIncorrectValueException("El límite superior "
                    + "de comida no puede ser menor que el límite inferior");
        }
        
        //Establecemos los datos pasados en el constructor
        this.numDias = numDias;
        this.limiteSuperiorComida = limiteSuperiorComida;
        this.limiteInferiorComida = limiteInferiorComida;
        
        //Valores por defecto
        alimentoInicial = 0;
        diaMax = 2;
        alimentoMax = 0;
        alimentoFinal = 0;
        
        //Inicializamos el manejador de eventos
        experimentoListener = new ArrayList<GraficaComidaChangeEventListener>();
    }
    
    /**
     * Suscribe un listener a este modelo de la población
     * @param l Listener que nos va a escuchar
     */
    public synchronized void addChangeEventListener(GraficaComidaChangeEventListener l) {
        if(l != null) {
            experimentoListener.add(l);
        }
    }
    
    /**
     * Desuscribe un listener a este modelo de la población
     * @param l Listener que nos va a dejar de escuchar
     */
    public synchronized void removeChangeEventListener(GraficaComidaChangeEventListener l) {
        if(l != null) {
            experimentoListener.remove(l);
        }
    }

    /**
     * Dispara las notificaciones a todos los listeners que nos escuchan
     * @param propertyName Propiedad que ha sido modificada
     * @param oldValue Viejo valor de la propiedad
     * @param newValue Nuevo valor de la propiedad
     */
    private void fireChangeEvent(String propertyName, int oldValue, int newValue) {
        //Notificamos de cualquier cambio en el modelo a todos los objetos que
        //se han suscrito a los cambios
        for(GraficaComidaChangeEventListener l : experimentoListener) {
            l.fireChangeEvent(new GraficaComidaChangeEvent(propertyName, oldValue, newValue));
        }
    }
    
    /**
     * Función que retorna el alimento inicial
     * @return Alimento incial
     */
    public int getAlimentoInicial() {
        return alimentoInicial;
    }

    /**
     * Función que establece el nuevo alimento inicial
     * @param alimentoInicial Alimento inicial
     * @throws GraficaComidaIncorrectValueException Si el alimento inicial no cumple los
     * valores se lanza la excepción
     */
    public void setAlimentoInicial(int alimentoInicial)
            throws GraficaComidaIncorrectValueException {
        if(alimentoInicial < limiteInferiorComida || alimentoInicial > limiteSuperiorComida ||
                alimentoInicial > getAlimentoMax()) {
            throw new GraficaComidaIncorrectValueException(
                    "Valor no comprendido entre el rango 0 y el límite superior");
        } else {
            if(this.alimentoInicial != alimentoInicial) {
                int oldValue = this.alimentoInicial;
                this.alimentoInicial = alimentoInicial;
                //Notificamos a todos los objetos que nos escuchan del cambio
                fireChangeEvent(GraficaComidaChangeEvent.ALIMENTO_INICIAL, 
                        oldValue, alimentoInicial);
            }
        }
    }

    /**
     * Función que retorna el día máximo
     * @return Día máximo
     */
    public int getDiaMax() {
        return diaMax;
    }

    /**
     * Función que establece el nuevo día máximo
     * @param diaMax Día máximo
     * @throws GraficaComidaIncorrectValueException  Si el día máximo no cumple los
     * valores se lanza la excepción
     */
    public void setDiaMax(int diaMax) throws GraficaComidaIncorrectValueException {
        //El día 1 está reservado para el alimento inicial
        if(diaMax < 2 || diaMax > numDias - 1) {
            throw new GraficaComidaIncorrectValueException(
                    "Valor no comprendido entre el rango de días permitidos");
        } else {
            if(this.diaMax != diaMax) {
                int oldValue = this.diaMax;
                this.diaMax = diaMax;
                //Notificamos a todos los objetos que nos escuchan del cambio
                fireChangeEvent(GraficaComidaChangeEvent.DIA_MAX, oldValue, diaMax);
            }
        }
    }

    /**
     * Función que retorna el alimento máximo
     * @return Alimento máximo
     */
    public int getAlimentoMax() {
        return alimentoMax;
    }

    /**
     * Función que establece el nuevo alimento máximo
     * @param alimentoMax Alimento máximo
     * @throws GraficaComidaIncorrectValueException  Si el alimento máximo no cumple los
     * valores se lanza la excepción
     */
    public void setAlimentoMax(int alimentoMax) throws GraficaComidaIncorrectValueException {
        if(alimentoMax < alimentoInicial || alimentoMax > limiteSuperiorComida ||
                alimentoMax < alimentoFinal) {
            throw new GraficaComidaIncorrectValueException(
                    "El alimento máximo no puede ser menor"
                    + " que el inicial o final, o mayor que el limite superior");
        } else {
            if(this.alimentoMax != alimentoMax) {
                int oldValue = this.alimentoMax;
                this.alimentoMax = alimentoMax;
                //Notificamos a todos los objetos que nos escuchan del cambio
                fireChangeEvent(GraficaComidaChangeEvent.ALIMENTO_MAX, oldValue, alimentoMax);
            }
        }
    }

    /**
     * Función que retorna el alimento final
     * @return Alimento final
     */
    public int getAlimentoFinal() {
        return alimentoFinal;
    }

    /**
     * Función que establece el nuevo alimento final
     * @param alimentoFinal Alimento final
     * @throws GraficaComidaIncorrectValueException  Si el alimento final no cumple los
     * valores se lanza la excepción
     */
    public void setAlimentoFinal(int alimentoFinal)
            throws GraficaComidaIncorrectValueException {
        if(alimentoFinal < limiteInferiorComida || alimentoFinal > getAlimentoMax()) {
            throw new GraficaComidaIncorrectValueException(
                    "El alimento final no puede ser menor"
                    + "que 0 o mayor que el alimento máximo");
        } else {
            if(this.alimentoFinal != alimentoFinal) {
                int oldValue = this.alimentoFinal;
                this.alimentoFinal = alimentoFinal;
                //Notificamos a todos los objetos que nos escuchan del cambio
                fireChangeEvent(GraficaComidaChangeEvent.ALIMENTO_FINAL,
                        oldValue, alimentoFinal);
            }
        }
    }
    
    /**
     * Establece el número de días que se mostrarán en la gráfica
     * @param numDias Mínimo de días 3
     */
    public void setNumDias(int numDias)
            throws GraficaComidaIncorrectValueException {
        if(numDias < 3 || numDias <= diaMax) {
            throw new GraficaComidaIncorrectValueException("No cumple las reglas de los"
                    + " valores");
        } else {
            if(this.numDias != numDias) {
                int oldValue = this.numDias;
                this.numDias = numDias;
                //Notificamos a todos los objetos que nos escuchan del cambio
                fireChangeEvent(GraficaComidaChangeEvent.NUM_DIAS,
                        oldValue, numDias);
            }
        }
    }
    
    /**
     * Devuelve el número de días que se están mostrando en la gráfica
     * @return int numDias
     */
    public int getNumDias() {
        return numDias;
    }

    /**
     * Establece el alimento mínimo de las bacterias
     * @param limiteInferiorComida El alimento mínimo no puede ser menor que 0
     */
    public void setAlimentoInferiorComida(int limiteInferiorComida)
            throws GraficaComidaIncorrectValueException {
        if(limiteInferiorComida < 0 || limiteInferiorComida < alimentoInicial ||
                limiteInferiorComida < alimentoFinal) {
            throw new GraficaComidaIncorrectValueException("No cumple las reglas de los"
                    + " valores");
        } else {
            if(this.limiteInferiorComida != limiteInferiorComida) {
                int oldValue = this.limiteInferiorComida;
                this.limiteInferiorComida = limiteInferiorComida;
                //Notificamos a todos los objetos que nos escuchan del cambio
                fireChangeEvent(GraficaComidaChangeEvent.MIN_ALIMENTO,
                        oldValue, limiteInferiorComida);
            }
        }
    }
    
    /**
     * Devuelve el alimento mínimo de las bacterias
     * @return int limiteInferiorComida
     */
    public int getLimiteInferiorComida() {
        return limiteInferiorComida;
    }

    /**
     * Establece el alimento máximo de las bacterias
     * @param limiteSuperiorComida Establece el alimento máximo el cual tiene
     * que ser mayor que cero
     */
    public void setLimiteSuperiorComida(int limiteSuperiorComida)
            throws GraficaComidaIncorrectValueException {
        if(limiteSuperiorComida <= 0 || limiteSuperiorComida < alimentoMax) {
            throw new GraficaComidaIncorrectValueException("No cumple las reglas de los"
                    + " valores");
        } else {
            if(this.limiteSuperiorComida != limiteSuperiorComida) {
                int oldValue = this.limiteSuperiorComida;
                this.limiteSuperiorComida = limiteSuperiorComida;
                //Notificamos a todos los objetos que nos escuchan del cambio
                fireChangeEvent(GraficaComidaChangeEvent.MAX_ALIMENTO,
                        oldValue, limiteSuperiorComida);
            }
        }
    }

    /**
     * Devuelve el alimento máximo de las bacterias
     * @return int limiteSuperiorComida
     */
    public int getLimiteSuperiorComida() {
        return limiteSuperiorComida;
    }


}
